Function

plot_fort_maps2 <- function(data, fill_str, legend_title, plot_title){
  require(ggplot2)
  require(ggsn)
  require(broom)
  ggplot() +                                               # initialize ggplot object
    geom_polygon(                                          # make a polygon
      data = data,                                    # data frame
      aes_string(x = "long", y = "lat", group = "group",                # coordinates, and group them by polygons
                 fill = fill_str),
      size=0.1, color="black") +                # variable to use for filling
    scale_fill_brewer(name=legend_title, palette = "RdYlBu", direction = -1,
                      drop = FALSE) + # fill with brewer colors   # add title
    theme(line = element_blank(),
          axis.text=element_blank(),      # .. tickmarks..
          axis.title=element_blank(),
          legend.position="none",
          plot.margin = unit(c(0, 0, 0, 0), "cm"),
          #legend.text=element_text(size=15),
          #legend.title=element_text(size=17), # .. axis labels..
          panel.background = element_blank(), plot.title = element_text(size=5)) + ggtitle(plot_title)
}
shape_to_ggplot <- function(shape){
  require(broom)
  gg_data <- tidy(shape)
  data <- slot(shape, "data")
  shape[["polyID"]] <- sapply(slot(shape, "polygons"), function(x) slot(x, "ID"))
  gg_data <- merge(gg_data, shape, by.x="id", by.y="polyID")
  return(gg_data)
}
grid_arrange_shared_legend <- function(..., ncol = length(list(...)), nrow = 1, position = c("bottom", "right")) {
  plots <- list(...)
  position <- match.arg(position)
  g <- ggplotGrob(plots[[1]] + theme(legend.position = position))$grobs
  legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]]
  lheight <- sum(legend$height)
  lwidth <- sum(legend$width)
  gl <- lapply(plots, function(x) x + theme(legend.position="none"))
  gl <- c(gl, ncol = ncol, nrow = nrow)
  combined <- switch(position,
                     "bottom" = arrangeGrob(do.call(arrangeGrob, gl),
                                            legend,
                                            ncol = 1,
                                            heights = unit.c(unit(1, "npc") - lheight, lheight)),
                     "right" = arrangeGrob(do.call(arrangeGrob, gl),
                                           legend,
                                           ncol = 2,
                                           widths = unit.c(unit(1, "npc") - lwidth, lwidth)))
  grid.newpage()
  grid.draw(combined)
  # return gtable invisibly
  invisible(combined)
}

Bairro Level Plots

bairro_shp <- readOGR("~/Documents/Harvard - SM80/Thesis/Fortaleza_Hom_RGit_PRIVATE_Files/Shapefiles/Shapefiles/Bairro/", "Bairros_from_CTs")
OGR data source with driver: ESRI Shapefile 
Source: "/Users/Sudipta/Documents/Harvard - SM80/Thesis/Fortaleza_Hom_RGit_PRIVATE_Files/Shapefiles/Shapefiles/Bairro", layer: "Bairros_from_CTs"
with 119 features
It has 4 fields
bairro_homs <- read_csv("~/Documents/Harvard - SM80/Thesis/Fortaleza_Hom_RGit_PRIVATE_Files/Bairro_SMR_IR_per_yr.csv", 
    col_types = cols(CD_GEOCODB = col_character()))
number of columns of result is not a multiple of vector length (arg 1)17 parsing failures.
row # A tibble: 5 x 5 col     row col   expected         actual file                                                 expected   <int> <chr> <chr>            <chr>  <chr>                                                actual 1  1854 pop   no trailing cha… e3     '~/Documents/Harvard - SM80/Thesis/Fortaleza_Hom_RG… file 2  1855 pop   no trailing cha… e3     '~/Documents/Harvard - SM80/Thesis/Fortaleza_Hom_RG… row 3  1856 pop   no trailing cha… e3     '~/Documents/Harvard - SM80/Thesis/Fortaleza_Hom_RG… col 4  1857 pop   no trailing cha… e3     '~/Documents/Harvard - SM80/Thesis/Fortaleza_Hom_RG… expected 5  1858 pop   no trailing cha… e3     '~/Documents/Harvard - SM80/Thesis/Fortaleza_Hom_RG…
... ................................. ... .......................................................................................... ........ ....................................................................................................................................................................................................................... ...... ................................................................................................................... .... ................................................................................................................... ... ................................................................................................................... ... ................................................................................................................... ........ ...................................................................................................................
See problems(...) for more details.
bairro_shp_gg <- shape_to_ggplot(bairro_shp)
Regions defined for each Polygons
column name ‘id’ is duplicated in the result

Aggregated plot of counts

bairro_agg_homs <- bairro_homs %>% group_by(CD_GEOCODB) %>% summarize(obs_count=sum(obs_count), SMR=mean(SMR_per_yr), IR=mean(IR_per_yr))
breaks <- quantile(bairro_agg_homs$obs_count, probs=c(seq(0,1,0.15),1))
bairro_shp_gg_count_agg <- bairro_shp_gg[,-1] %>% left_join(bairro_agg_homs[,c(1,2)], by=c("cod_ibge"="CD_GEOCODB")) %>% mutate(cut=cut(obs_count, breaks=breaks, include.lowest=TRUE))
Column `cod_ibge`/`CD_GEOCODB` joining factor and character vector, coercing into character vector
plot_fort_maps2(bairro_shp_gg_count_agg, "cut","Homicide counts", "Aggregate counts for 2001-17 by bairro") + theme(plot.title = element_text(size=10), legend.position = "right")

Aggregated plot of SMRs

breaks <- quantile(bairro_agg_homs$SMR, probs=c(seq(0,1,0.15),1))
bairro_shp_gg_count_agg <- bairro_shp_gg[,-1] %>% left_join(bairro_agg_homs[,c(1,3)], by=c("cod_ibge"="CD_GEOCODB")) %>% mutate(cut=cut(SMR, breaks=breaks, include.lowest=TRUE))
Column `cod_ibge`/`CD_GEOCODB` joining factor and character vector, coercing into character vector
plot_fort_maps2(bairro_shp_gg_count_agg, "cut","Homicide counts", "Aggregate SMR for 2001-17 by bairro") + theme(plot.title = element_text(size=10), legend.position = "right")

Aggregated plot of IRs

breaks <- quantile(bairro_agg_homs$IR, probs=c(seq(0,1,0.15),1))
bairro_shp_gg_count_agg <- bairro_shp_gg[,-1] %>% left_join(bairro_agg_homs[,c(1,4)], by=c("cod_ibge"="CD_GEOCODB")) %>% mutate(cut=cut(IR, breaks=breaks, include.lowest=TRUE))
Column `cod_ibge`/`CD_GEOCODB` joining factor and character vector, coercing into character vector
plot_fort_maps2(bairro_shp_gg_count_agg, "cut","Homicide counts", "Aggregate IR for 2001-17 by bairro") + theme(plot.title = element_text(size=10), legend.position = "right")

Count plots by year

breaks <- quantile(bairro_homs$obs_count, probs=c(seq(0,1,0.15),1))
bairro_shp_gg_count <- bairro_shp_gg[,-1] %>% left_join(spread(bairro_homs[,c(1:3)], YOD, obs_count), by=c("cod_ibge"="CD_GEOCODB")) %>% mutate_at(.funs = funs(cut = cut(., breaks=breaks, include.lowest=TRUE)), .vars = vars(`2001`:`2017`))
Column `cod_ibge`/`CD_GEOCODB` joining factor and character vector, coercing into character vector
count_plots <- sapply(2001:2017, function(x) plot_fort_maps2(bairro_shp_gg_count, paste0("`",x,"_cut`"), paste0("Homicide count for ",x), as.character(x)), simplify = FALSE)
library(gridExtra)
library(grid)
library(ggpubr)
library(cowplot)
plot <- count_plots[[1]] + theme(legend.position = "right", legend.text=element_text(size=5), legend.title=element_text(size=5), legend.key.size = unit(0.5,"line")) + guides(fill=guide_legend(title="Homicide Count"))
mylegend <- g_legend(plot)
grid_count <- do.call((plot_grid), c(count_plots, nrow=5)) + draw_grob(mylegend, 0.28, 0.08, 0.28, 0.08) 

IR plots by years

breaks <- quantile(bairro_homs$IR_per_yr, probs=c(seq(0,1,0.15),1), na.rm=TRUE)
bairro_shp_gg_IR <- bairro_shp_gg[,-1] %>% left_join(spread(bairro_homs[,c(1,2,7)], YOD, IR_per_yr), by=c("cod_ibge"="CD_GEOCODB")) %>% mutate_at(.funs = funs(cut = cut(., breaks=breaks, include.lowest=TRUE)), .vars = vars(`2001`:`2017`))
Column `cod_ibge`/`CD_GEOCODB` joining factor and character vector, coercing into character vector
IR_plots <- sapply(2001:2017, function(x) plot_fort_maps2(bairro_shp_gg_IR, paste0("`",x,"_cut`"), paste0("IR for ",x), as.character(x)), simplify = FALSE)
plot <- IR_plots[[1]] + theme(legend.position = "right", legend.text=element_text(size=5), legend.title=element_text(size=5), legend.key.size = unit(0.5,"line")) + guides(fill=guide_legend(title="Homicide IR"))
mylegend <- g_legend(plot)
grid_IR <- do.call((plot_grid), c(count_plots, nrow=5)) + draw_grob(mylegend, 0.28, 0.08, 0.28, 0.08)

SMR plots by years

breaks <- quantile(bairro_homs$SMR_per_yr, probs=c(seq(0,1,0.15),1), na.rm=TRUE)
bairro_shp_gg_SMR <- bairro_shp_gg[,-1] %>% left_join(spread(bairro_homs[,c(1,2,6)], YOD, SMR_per_yr), by=c("cod_ibge"="CD_GEOCODB")) %>% mutate_at(.funs = funs(cut = cut(., breaks=breaks, include.lowest=TRUE)), .vars = vars(`2001`:`2017`))
Column `cod_ibge`/`CD_GEOCODB` joining factor and character vector, coercing into character vector
SMR_plots <- sapply(2001:2017, function(x) plot_fort_maps2(bairro_shp_gg_SMR, paste0("`",x,"_cut`"), paste0("IR for ",x), as.character(x)), simplify = FALSE)
plot <- SMR_plots[[1]] + theme(legend.position = "right", legend.text=element_text(size=5), legend.title=element_text(size=5), legend.key.size = unit(0.5,"line")) + guides(fill=guide_legend(title="Homicide IR"))
mylegend <- g_legend(plot)
grid_SMR <- do.call((plot_grid), c(count_plots, nrow=5)) + draw_grob(mylegend, 0.28, 0.08, 0.28, 0.08)

CT level plots

CT_shp <- readOGR("~/Documents/Harvard - SM80/Thesis/Fortaleza_Hom_RGit_PRIVATE_Files/Shapefiles/Shapefiles/CTs/", "Corrected_CTs")
OGR data source with driver: ESRI Shapefile 
Source: "/Users/Sudipta/Documents/Harvard - SM80/Thesis/Fortaleza_Hom_RGit_PRIVATE_Files/Shapefiles/Shapefiles/CTs", layer: "Corrected_CTs"
with 3044 features
It has 13 fields
CT_homs <- read_csv("~/Documents/Harvard - SM80/Thesis/Fortaleza_Hom_RGit_PRIVATE_Files/CT_SMR_IR_per_yr-agg.csv", 
    col_types = cols(CD_GEOCODI = col_character()))
CT_shp_gg <- shape_to_ggplot(CT_shp)
Regions defined for each Polygons

Some CTs have homicides but non pop. We have to revisit these. For now, I am turning Inf IR and SMR to zero. At most there are 2 homicides per year in these CTs. Also NaN are pop zero, hom zero. Also turning these to zero.

CT_homs <- CT_homs %>% mutate(SMR_per_yr=replace(SMR_per_yr, SMR_per_yr==Inf | is.nan(SMR_per_yr), 0), IR_per_yr=replace(IR_per_yr, IR_per_yr==Inf | is.nan(IR_per_yr), 0)) %>% as.data.frame()

Aggregated plot of counts

CT_agg_homs <- CT_homs %>% group_by(CD_GEOCODI) %>% summarize(obs_count=sum(obs_count), SMR=mean(SMR_per_yr), IR=mean(IR_per_yr))
breaks <- quantile(CT_agg_homs$obs_count, probs=c(seq(0,1,0.15),1))
CT_shp_gg_count_agg <- CT_shp_gg[,-1] %>% left_join(CT_agg_homs[,c(1,2)], by="CD_GEOCODI") %>% mutate(cut=cut(obs_count, breaks=breaks, include.lowest=TRUE))
Column `CD_GEOCODI` joining factor and character vector, coercing into character vector
plot_fort_maps2(CT_shp_gg_count_agg, "cut","Homicide counts", "Aggregate counts for 2001-17 by CT") + theme(plot.title = element_text(size=10), legend.position = "right")

Aggregated plot of SMRs

breaks <- quantile(CT_agg_homs$SMR, probs=c(seq(0,1,0.15),1))
CT_shp_gg_count_agg <- CT_shp_gg[,-1] %>% left_join(CT_agg_homs[,c(1,3)], by="CD_GEOCODI") %>% mutate(cut=cut(SMR, breaks=breaks, include.lowest=TRUE))
Column `CD_GEOCODI` joining factor and character vector, coercing into character vector
plot_fort_maps2(CT_shp_gg_count_agg, "cut","Homicide counts", "Aggregate SMR for 2001-17 by CT") + theme(plot.title = element_text(size=10), legend.position = "right")

Aggregated plot of IRs

breaks <- quantile(CT_agg_homs$IR, probs=c(seq(0,1,0.15),1))
CT_shp_gg_count_agg <- CT_shp_gg[,-1] %>% left_join(CT_agg_homs[,c(1,4)], by="CD_GEOCODI") %>% mutate(cut=cut(IR, breaks=breaks, include.lowest=TRUE))
Column `CD_GEOCODI` joining factor and character vector, coercing into character vector
plot_fort_maps2(CT_shp_gg_count_agg, "cut","Homicide counts", "Aggregate IR for 2001-17 by CT") + theme(plot.title = element_text(size=10), legend.position = "right")

Count plots by year

breaks <- c(0,1,2,3,4,10,35)
CT_shp_gg_count <- CT_shp_gg[,-1] %>% left_join(spread(CT_homs[,c(1:3)], YOD, obs_count), by="CD_GEOCODI") %>% mutate_at(.funs = funs(cut = cut(., breaks=breaks, include.lowest=TRUE)), .vars = vars(`2001`:`2017`))
Column `CD_GEOCODI` joining factor and character vector, coercing into character vector
count_plots <- sapply(2001:2017, function(x) plot_fort_maps2(CT_shp_gg_count, paste0("`",x,"_cut`"), paste0("Homicide count for ",x), as.character(x), size=0.05), simplify = FALSE)
library(gridExtra)
library(grid)
library(ggpubr)
library(cowplot)
plot <- count_plots[[1]] + theme(legend.position = "right", legend.text=element_text(size=5), legend.title=element_text(size=5), legend.key.size = unit(0.5,"line")) + guides(fill=guide_legend(title="Homicide Count"))
mylegend <- g_legend(plot)
grid_count <- do.call((plot_grid), c(count_plots, nrow=5)) + draw_grob(mylegend, 0.28, 0.08, 0.28, 0.08) 
grid_count

IR plots by years

breaks <- c(0,5,10,15,50,100,200,15000)
CT_shp_gg_IR <- CT_shp_gg[,-1] %>% left_join(spread(CT_homs[,c(1,2,7)], YOD, IR_per_yr), by="CD_GEOCODI") %>% mutate_at(.funs = funs(cut = cut(., breaks=breaks, labels=(c("[0,5]","(5,10]","(10,15]","(15,50]","(50,100]","(100,200]",">200")), include.lowest=TRUE)), .vars = vars(`2001`:`2017`))
Column `CD_GEOCODI` joining factor and character vector, coercing into character vector
IR_plots <- sapply(2001:2017, function(x) plot_fort_maps2(CT_shp_gg_IR, paste0("`",x,"_cut`"), paste0("IR for ",x), as.character(x)), simplify = FALSE)
plot <- IR_plots[[1]] + theme(legend.position = "right", legend.text=element_text(size=5), legend.title=element_text(size=5), legend.key.size = unit(0.5,"line")) + guides(fill=guide_legend(title="Homicide IR"))
mylegend <- g_legend(plot)
grid_IR <- do.call((plot_grid), c(count_plots, nrow=5)) + draw_grob(mylegend, 0.28, 0.08, 0.28, 0.08)
grid_IR

SMR plots by years

breaks <- c(0,1,2,5,10,15,50,400)
CT_shp_gg_SMR <- CT_shp_gg[,-1] %>% left_join(spread(CT_homs[,c(1,2,6)], YOD, SMR_per_yr), by="CD_GEOCODI") %>% mutate_at(.funs = funs(cut = cut(., breaks=breaks, labels=(c("[0,1]","(1,2]","(2,5]","(5,10]","(10,15]","(15,50]",">50")), include.lowest=TRUE)), .vars = vars(`2001`:`2017`))
Column `CD_GEOCODI` joining factor and character vector, coercing into character vector
SMR_plots <- sapply(2001:2017, function(x) plot_fort_maps2(CT_shp_gg_SMR, paste0("`",x,"_cut`"), paste0("IR for ",x), as.character(x)), simplify = FALSE)
plot <- SMR_plots[[1]] + theme(legend.position = "right", legend.text=element_text(size=5), legend.title=element_text(size=5), legend.key.size = unit(0.5,"line")) + guides(fill=guide_legend(title="Homicide IR"))
mylegend <- g_legend(plot)
grid_SMR <- do.call((plot_grid), c(count_plots, nrow=5)) + draw_grob(mylegend, 0.28, 0.08, 0.28, 0.08)
grid_SMR

LS0tCnRpdGxlOiAiRGVzY3JpcHRpdmUgTWFwcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKRnVuY3Rpb24KYGBge3J9CnBsb3RfZm9ydF9tYXBzMiA8LSBmdW5jdGlvbihkYXRhLCBmaWxsX3N0ciwgbGVnZW5kX3RpdGxlLCBwbG90X3RpdGxlLCBzaXplPTAuMSl7CiAgcmVxdWlyZShnZ3Bsb3QyKQogIHJlcXVpcmUoZ2dzbikKICByZXF1aXJlKGJyb29tKQogIGdncGxvdCgpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW5pdGlhbGl6ZSBnZ3Bsb3Qgb2JqZWN0CiAgICBnZW9tX3BvbHlnb24oICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBtYWtlIGEgcG9seWdvbgogICAgICBkYXRhID0gZGF0YSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGRhdGEgZnJhbWUKICAgICAgYWVzX3N0cmluZyh4ID0gImxvbmciLCB5ID0gImxhdCIsIGdyb3VwID0gImdyb3VwIiwgICAgICAgICAgICAgICAgIyBjb29yZGluYXRlcywgYW5kIGdyb3VwIHRoZW0gYnkgcG9seWdvbnMKICAgICAgICAgICAgICAgICBmaWxsID0gZmlsbF9zdHIpLAogICAgICBzaXplPXNpemUsIGNvbG9yPSJibGFjayIpICsgICAgICAgICAgICAgICAgIyB2YXJpYWJsZSB0byB1c2UgZm9yIGZpbGxpbmcKICAgIHNjYWxlX2ZpbGxfYnJld2VyKG5hbWU9bGVnZW5kX3RpdGxlLCBwYWxldHRlID0gIlJkWWxCdSIsIGRpcmVjdGlvbiA9IC0xLAogICAgICAgICAgICAgICAgICAgICAgZHJvcCA9IEZBTFNFKSArICMgZmlsbCB3aXRoIGJyZXdlciBjb2xvcnMgICAjIGFkZCB0aXRsZQogICAgdGhlbWUobGluZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMudGV4dD1lbGVtZW50X2JsYW5rKCksICAgICAgIyAuLiB0aWNrbWFya3MuLgogICAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBsZWdlbmQucG9zaXRpb249Im5vbmUiLAogICAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMCwgMCwgMCwgMCksICJjbSIpLAogICAgICAgICAgI2xlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTE1KSwKICAgICAgICAgICNsZWdlbmQudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTcpLCAjIC4uIGF4aXMgbGFiZWxzLi4KICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT01KSkgKyBnZ3RpdGxlKHBsb3RfdGl0bGUpCn0KCgoKc2hhcGVfdG9fZ2dwbG90IDwtIGZ1bmN0aW9uKHNoYXBlKXsKICByZXF1aXJlKGJyb29tKQogIGdnX2RhdGEgPC0gdGlkeShzaGFwZSkKICBkYXRhIDwtIHNsb3Qoc2hhcGUsICJkYXRhIikKICBzaGFwZVtbInBvbHlJRCJdXSA8LSBzYXBwbHkoc2xvdChzaGFwZSwgInBvbHlnb25zIiksIGZ1bmN0aW9uKHgpIHNsb3QoeCwgIklEIikpCiAgZ2dfZGF0YSA8LSBtZXJnZShnZ19kYXRhLCBzaGFwZSwgYnkueD0iaWQiLCBieS55PSJwb2x5SUQiKQogIHJldHVybihnZ19kYXRhKQp9CmdyaWRfYXJyYW5nZV9zaGFyZWRfbGVnZW5kIDwtIGZ1bmN0aW9uKC4uLiwgbmNvbCA9IGxlbmd0aChsaXN0KC4uLikpLCBucm93ID0gMSwgcG9zaXRpb24gPSBjKCJib3R0b20iLCAicmlnaHQiKSkgewoKICBwbG90cyA8LSBsaXN0KC4uLikKICBwb3NpdGlvbiA8LSBtYXRjaC5hcmcocG9zaXRpb24pCiAgZyA8LSBnZ3Bsb3RHcm9iKHBsb3RzW1sxXV0gKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBwb3NpdGlvbikpJGdyb2JzCiAgbGVnZW5kIDwtIGdbW3doaWNoKHNhcHBseShnLCBmdW5jdGlvbih4KSB4JG5hbWUpID09ICJndWlkZS1ib3giKV1dCiAgbGhlaWdodCA8LSBzdW0obGVnZW5kJGhlaWdodCkKICBsd2lkdGggPC0gc3VtKGxlZ2VuZCR3aWR0aCkKICBnbCA8LSBsYXBwbHkocGxvdHMsIGZ1bmN0aW9uKHgpIHggKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSkKICBnbCA8LSBjKGdsLCBuY29sID0gbmNvbCwgbnJvdyA9IG5yb3cpCgogIGNvbWJpbmVkIDwtIHN3aXRjaChwb3NpdGlvbiwKICAgICAgICAgICAgICAgICAgICAgImJvdHRvbSIgPSBhcnJhbmdlR3JvYihkby5jYWxsKGFycmFuZ2VHcm9iLCBnbCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlaWdodHMgPSB1bml0LmModW5pdCgxLCAibnBjIikgLSBsaGVpZ2h0LCBsaGVpZ2h0KSksCiAgICAgICAgICAgICAgICAgICAgICJyaWdodCIgPSBhcnJhbmdlR3JvYihkby5jYWxsKGFycmFuZ2VHcm9iLCBnbCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdpZHRocyA9IHVuaXQuYyh1bml0KDEsICJucGMiKSAtIGx3aWR0aCwgbHdpZHRoKSkpCgogIGdyaWQubmV3cGFnZSgpCiAgZ3JpZC5kcmF3KGNvbWJpbmVkKQoKICAjIHJldHVybiBndGFibGUgaW52aXNpYmx5CiAgaW52aXNpYmxlKGNvbWJpbmVkKQoKfQpgYGAKCiMjI0JhaXJybyBMZXZlbCBQbG90cwoKYGBge3J9CmJhaXJyb19zaHAgPC0gcmVhZE9HUigifi9Eb2N1bWVudHMvSGFydmFyZCAtIFNNODAvVGhlc2lzL0ZvcnRhbGV6YV9Ib21fUkdpdF9QUklWQVRFX0ZpbGVzL1NoYXBlZmlsZXMvU2hhcGVmaWxlcy9CYWlycm8vIiwgIkJhaXJyb3NfZnJvbV9DVHMiKQpgYGAKCmBgYHtyfQpiYWlycm9faG9tcyA8LSByZWFkX2Nzdigifi9Eb2N1bWVudHMvSGFydmFyZCAtIFNNODAvVGhlc2lzL0ZvcnRhbGV6YV9Ib21fUkdpdF9QUklWQVRFX0ZpbGVzL0JhaXJyb19TTVJfSVJfcGVyX3lyLmNzdiIsIAogICAgY29sX3R5cGVzID0gY29scyhDRF9HRU9DT0RCID0gY29sX2NoYXJhY3RlcigpKSkKYGBgCmBgYHtyfQpiYWlycm9fc2hwX2dnIDwtIHNoYXBlX3RvX2dncGxvdChiYWlycm9fc2hwKQpgYGAKCkFnZ3JlZ2F0ZWQgcGxvdCBvZiBjb3VudHMKCmBgYHtyfQpiYWlycm9fYWdnX2hvbXMgPC0gYmFpcnJvX2hvbXMgJT4lIGdyb3VwX2J5KENEX0dFT0NPREIpICU+JSBzdW1tYXJpemUob2JzX2NvdW50PXN1bShvYnNfY291bnQpLCBTTVI9bWVhbihTTVJfcGVyX3lyKSwgSVI9bWVhbihJUl9wZXJfeXIpKQpgYGAKCmBgYHtyfQpicmVha3MgPC0gcXVhbnRpbGUoYmFpcnJvX2FnZ19ob21zJG9ic19jb3VudCwgcHJvYnM9YyhzZXEoMCwxLDAuMTUpLDEpKQpiYWlycm9fc2hwX2dnX2NvdW50X2FnZyA8LSBiYWlycm9fc2hwX2dnWywtMV0gJT4lIGxlZnRfam9pbihiYWlycm9fYWdnX2hvbXNbLGMoMSwyKV0sIGJ5PWMoImNvZF9pYmdlIj0iQ0RfR0VPQ09EQiIpKSAlPiUgbXV0YXRlKGN1dD1jdXQob2JzX2NvdW50LCBicmVha3M9YnJlYWtzLCBpbmNsdWRlLmxvd2VzdD1UUlVFKSkKcGxvdF9mb3J0X21hcHMyKGJhaXJyb19zaHBfZ2dfY291bnRfYWdnLCAiY3V0IiwiSG9taWNpZGUgY291bnRzIiwgIkFnZ3JlZ2F0ZSBjb3VudHMgZm9yIDIwMDEtMTcgYnkgYmFpcnJvIikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTApLCBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCkFnZ3JlZ2F0ZWQgcGxvdCBvZiBTTVJzCgpgYGB7cn0KYnJlYWtzIDwtIHF1YW50aWxlKGJhaXJyb19hZ2dfaG9tcyRTTVIsIHByb2JzPWMoc2VxKDAsMSwwLjE1KSwxKSkKYmFpcnJvX3NocF9nZ19jb3VudF9hZ2cgPC0gYmFpcnJvX3NocF9nZ1ssLTFdICU+JSBsZWZ0X2pvaW4oYmFpcnJvX2FnZ19ob21zWyxjKDEsMyldLCBieT1jKCJjb2RfaWJnZSI9IkNEX0dFT0NPREIiKSkgJT4lIG11dGF0ZShjdXQ9Y3V0KFNNUiwgYnJlYWtzPWJyZWFrcywgaW5jbHVkZS5sb3dlc3Q9VFJVRSkpCnBsb3RfZm9ydF9tYXBzMihiYWlycm9fc2hwX2dnX2NvdW50X2FnZywgImN1dCIsIkhvbWljaWRlIGNvdW50cyIsICJBZ2dyZWdhdGUgU01SIGZvciAyMDAxLTE3IGJ5IGJhaXJybyIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSwgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgpBZ2dyZWdhdGVkIHBsb3Qgb2YgSVJzCgpgYGB7cn0KYnJlYWtzIDwtIHF1YW50aWxlKGJhaXJyb19hZ2dfaG9tcyRJUiwgcHJvYnM9YyhzZXEoMCwxLDAuMTUpLDEpKQpiYWlycm9fc2hwX2dnX2NvdW50X2FnZyA8LSBiYWlycm9fc2hwX2dnWywtMV0gJT4lIGxlZnRfam9pbihiYWlycm9fYWdnX2hvbXNbLGMoMSw0KV0sIGJ5PWMoImNvZF9pYmdlIj0iQ0RfR0VPQ09EQiIpKSAlPiUgbXV0YXRlKGN1dD1jdXQoSVIsIGJyZWFrcz1icmVha3MsIGluY2x1ZGUubG93ZXN0PVRSVUUpKQpwbG90X2ZvcnRfbWFwczIoYmFpcnJvX3NocF9nZ19jb3VudF9hZ2csICJjdXQiLCJIb21pY2lkZSBjb3VudHMiLCAiQWdncmVnYXRlIElSIGZvciAyMDAxLTE3IGJ5IGJhaXJybyIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSwgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgoKQ291bnQgcGxvdHMgYnkgeWVhcgpgYGB7cn0KYnJlYWtzIDwtIHF1YW50aWxlKGJhaXJyb19ob21zJG9ic19jb3VudCwgcHJvYnM9YyhzZXEoMCwxLDAuMTUpLDEpKQoKYmFpcnJvX3NocF9nZ19jb3VudCA8LSBiYWlycm9fc2hwX2dnWywtMV0gJT4lIGxlZnRfam9pbihzcHJlYWQoYmFpcnJvX2hvbXNbLGMoMTozKV0sIFlPRCwgb2JzX2NvdW50KSwgYnk9YygiY29kX2liZ2UiPSJDRF9HRU9DT0RCIikpICU+JSBtdXRhdGVfYXQoLmZ1bnMgPSBmdW5zKGN1dCA9IGN1dCguLCBicmVha3M9YnJlYWtzLCBpbmNsdWRlLmxvd2VzdD1UUlVFKSksIC52YXJzID0gdmFycyhgMjAwMWA6YDIwMTdgKSkKYGBgCgpgYGB7cn0KY291bnRfcGxvdHMgPC0gc2FwcGx5KDIwMDE6MjAxNywgZnVuY3Rpb24oeCkgcGxvdF9mb3J0X21hcHMyKGJhaXJyb19zaHBfZ2dfY291bnQsIHBhc3RlMCgiYCIseCwiX2N1dGAiKSwgcGFzdGUwKCJIb21pY2lkZSBjb3VudCBmb3IgIix4KSwgYXMuY2hhcmFjdGVyKHgpKSwgc2ltcGxpZnkgPSBGQUxTRSkKYGBgCgoKYGBge3J9CmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KGNvd3Bsb3QpCnBsb3QgPC0gY291bnRfcGxvdHNbWzFdXSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTUpLCBsZWdlbmQudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9NSksIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41LCJsaW5lIikpICsgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPSJIb21pY2lkZSBDb3VudCIpKQpteWxlZ2VuZCA8LSBnX2xlZ2VuZChwbG90KQpncmlkX2NvdW50IDwtIGRvLmNhbGwoKHBsb3RfZ3JpZCksIGMoY291bnRfcGxvdHMsIG5yb3c9NSkpICsgZHJhd19ncm9iKG15bGVnZW5kLCAwLjI4LCAwLjA4LCAwLjI4LCAwLjA4KSAKYGBgCgoKSVIgcGxvdHMgYnkgeWVhcnMKCmBgYHtyfQpicmVha3MgPC0gcXVhbnRpbGUoYmFpcnJvX2hvbXMkSVJfcGVyX3lyLCBwcm9icz1jKHNlcSgwLDEsMC4xNSksMSksIG5hLnJtPVRSVUUpCgpiYWlycm9fc2hwX2dnX0lSIDwtIGJhaXJyb19zaHBfZ2dbLC0xXSAlPiUgbGVmdF9qb2luKHNwcmVhZChiYWlycm9faG9tc1ssYygxLDIsNyldLCBZT0QsIElSX3Blcl95ciksIGJ5PWMoImNvZF9pYmdlIj0iQ0RfR0VPQ09EQiIpKSAlPiUgbXV0YXRlX2F0KC5mdW5zID0gZnVucyhjdXQgPSBjdXQoLiwgYnJlYWtzPWJyZWFrcywgaW5jbHVkZS5sb3dlc3Q9VFJVRSkpLCAudmFycyA9IHZhcnMoYDIwMDFgOmAyMDE3YCkpCmBgYAoKYGBge3J9CklSX3Bsb3RzIDwtIHNhcHBseSgyMDAxOjIwMTcsIGZ1bmN0aW9uKHgpIHBsb3RfZm9ydF9tYXBzMihiYWlycm9fc2hwX2dnX0lSLCBwYXN0ZTAoImAiLHgsIl9jdXRgIiksIHBhc3RlMCgiSVIgZm9yICIseCksIGFzLmNoYXJhY3Rlcih4KSksIHNpbXBsaWZ5ID0gRkFMU0UpCgpwbG90IDwtIElSX3Bsb3RzW1sxXV0gKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT01KSwgbGVnZW5kLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTUpLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNSwibGluZSIpKSArIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZCh0aXRsZT0iSG9taWNpZGUgSVIiKSkKbXlsZWdlbmQgPC0gZ19sZWdlbmQocGxvdCkKZ3JpZF9JUiA8LSBkby5jYWxsKChwbG90X2dyaWQpLCBjKGNvdW50X3Bsb3RzLCBucm93PTUpKSArIGRyYXdfZ3JvYihteWxlZ2VuZCwgMC4yOCwgMC4wOCwgMC4yOCwgMC4wOCkKYGBgCgoKU01SIHBsb3RzIGJ5IHllYXJzCmBgYHtyfQpicmVha3MgPC0gcXVhbnRpbGUoYmFpcnJvX2hvbXMkU01SX3Blcl95ciwgcHJvYnM9YyhzZXEoMCwxLDAuMTUpLDEpLCBuYS5ybT1UUlVFKQoKYmFpcnJvX3NocF9nZ19TTVIgPC0gYmFpcnJvX3NocF9nZ1ssLTFdICU+JSBsZWZ0X2pvaW4oc3ByZWFkKGJhaXJyb19ob21zWyxjKDEsMiw2KV0sIFlPRCwgU01SX3Blcl95ciksIGJ5PWMoImNvZF9pYmdlIj0iQ0RfR0VPQ09EQiIpKSAlPiUgbXV0YXRlX2F0KC5mdW5zID0gZnVucyhjdXQgPSBjdXQoLiwgYnJlYWtzPWJyZWFrcywgaW5jbHVkZS5sb3dlc3Q9VFJVRSkpLCAudmFycyA9IHZhcnMoYDIwMDFgOmAyMDE3YCkpCmBgYAoKYGBge3J9ClNNUl9wbG90cyA8LSBzYXBwbHkoMjAwMToyMDE3LCBmdW5jdGlvbih4KSBwbG90X2ZvcnRfbWFwczIoYmFpcnJvX3NocF9nZ19TTVIsIHBhc3RlMCgiYCIseCwiX2N1dGAiKSwgcGFzdGUwKCJJUiBmb3IgIix4KSwgYXMuY2hhcmFjdGVyKHgpKSwgc2ltcGxpZnkgPSBGQUxTRSkKCnBsb3QgPC0gU01SX3Bsb3RzW1sxXV0gKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT01KSwgbGVnZW5kLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTUpLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNSwibGluZSIpKSArIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZCh0aXRsZT0iSG9taWNpZGUgSVIiKSkKbXlsZWdlbmQgPC0gZ19sZWdlbmQocGxvdCkKZ3JpZF9TTVIgPC0gZG8uY2FsbCgocGxvdF9ncmlkKSwgYyhjb3VudF9wbG90cywgbnJvdz01KSkgKyBkcmF3X2dyb2IobXlsZWdlbmQsIDAuMjgsIDAuMDgsIDAuMjgsIDAuMDgpCmBgYAoKCiMjIyBDVCBsZXZlbCBwbG90cwoKCgpgYGB7cn0KQ1Rfc2hwIDwtIHJlYWRPR1IoIn4vRG9jdW1lbnRzL0hhcnZhcmQgLSBTTTgwL1RoZXNpcy9Gb3J0YWxlemFfSG9tX1JHaXRfUFJJVkFURV9GaWxlcy9TaGFwZWZpbGVzL1NoYXBlZmlsZXMvQ1RzLyIsICJDb3JyZWN0ZWRfQ1RzIikKYGBgCgpgYGB7cn0KQ1RfaG9tcyA8LSByZWFkX2Nzdigifi9Eb2N1bWVudHMvSGFydmFyZCAtIFNNODAvVGhlc2lzL0ZvcnRhbGV6YV9Ib21fUkdpdF9QUklWQVRFX0ZpbGVzL0NUX1NNUl9JUl9wZXJfeXItYWdnLmNzdiIsIAogICAgY29sX3R5cGVzID0gY29scyhDRF9HRU9DT0RJID0gY29sX2NoYXJhY3RlcigpKSkKYGBgCmBgYHtyfQpDVF9zaHBfZ2cgPC0gc2hhcGVfdG9fZ2dwbG90KENUX3NocCkKYGBgClNvbWUgQ1RzIGhhdmUgaG9taWNpZGVzIGJ1dCBub24gcG9wLiBXZSBoYXZlIHRvIHJldmlzaXQgdGhlc2UuIEZvciBub3csIEkgYW0gdHVybmluZyBJbmYgSVIgYW5kIFNNUiB0byB6ZXJvLiBBdCBtb3N0IHRoZXJlIGFyZSAyIGhvbWljaWRlcyBwZXIgeWVhciBpbiB0aGVzZSBDVHMuIEFsc28gTmFOIGFyZSBwb3AgemVybywgaG9tIHplcm8uIEFsc28gdHVybmluZyB0aGVzZSB0byB6ZXJvLgoKYGBge3J9CkNUX2hvbXMgPC0gQ1RfaG9tcyAlPiUgbXV0YXRlKFNNUl9wZXJfeXI9cmVwbGFjZShTTVJfcGVyX3lyLCBTTVJfcGVyX3lyPT1JbmYgfCBpcy5uYW4oU01SX3Blcl95ciksIDApLCBJUl9wZXJfeXI9cmVwbGFjZShJUl9wZXJfeXIsIElSX3Blcl95cj09SW5mIHwgaXMubmFuKElSX3Blcl95ciksIDApKSAlPiUgYXMuZGF0YS5mcmFtZSgpCmBgYAoKCkFnZ3JlZ2F0ZWQgcGxvdCBvZiBjb3VudHMKCmBgYHtyfQpDVF9hZ2dfaG9tcyA8LSBDVF9ob21zICU+JSBncm91cF9ieShDRF9HRU9DT0RJKSAlPiUgc3VtbWFyaXplKG9ic19jb3VudD1zdW0ob2JzX2NvdW50KSwgU01SPW1lYW4oU01SX3Blcl95ciksIElSPW1lYW4oSVJfcGVyX3lyKSkKYGBgCgpgYGB7cn0KYnJlYWtzIDwtIHF1YW50aWxlKENUX2FnZ19ob21zJG9ic19jb3VudCwgcHJvYnM9YyhzZXEoMCwxLDAuMTUpLDEpKQpDVF9zaHBfZ2dfY291bnRfYWdnIDwtIENUX3NocF9nZ1ssLTFdICU+JSBsZWZ0X2pvaW4oQ1RfYWdnX2hvbXNbLGMoMSwyKV0sIGJ5PSJDRF9HRU9DT0RJIikgJT4lIG11dGF0ZShjdXQ9Y3V0KG9ic19jb3VudCwgYnJlYWtzPWJyZWFrcywgaW5jbHVkZS5sb3dlc3Q9VFJVRSkpCnBsb3RfZm9ydF9tYXBzMihDVF9zaHBfZ2dfY291bnRfYWdnLCAiY3V0IiwiSG9taWNpZGUgY291bnRzIiwgIkFnZ3JlZ2F0ZSBjb3VudHMgZm9yIDIwMDEtMTcgYnkgQ1QiKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCksIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKQWdncmVnYXRlZCBwbG90IG9mIFNNUnMKCmBgYHtyfQpicmVha3MgPC0gcXVhbnRpbGUoQ1RfYWdnX2hvbXMkU01SLCBwcm9icz1jKHNlcSgwLDEsMC4xNSksMSkpCkNUX3NocF9nZ19jb3VudF9hZ2cgPC0gQ1Rfc2hwX2dnWywtMV0gJT4lIGxlZnRfam9pbihDVF9hZ2dfaG9tc1ssYygxLDMpXSwgYnk9IkNEX0dFT0NPREkiKSAlPiUgbXV0YXRlKGN1dD1jdXQoU01SLCBicmVha3M9YnJlYWtzLCBpbmNsdWRlLmxvd2VzdD1UUlVFKSkKcGxvdF9mb3J0X21hcHMyKENUX3NocF9nZ19jb3VudF9hZ2csICJjdXQiLCJIb21pY2lkZSBjb3VudHMiLCAiQWdncmVnYXRlIFNNUiBmb3IgMjAwMS0xNyBieSBDVCIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSwgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgpBZ2dyZWdhdGVkIHBsb3Qgb2YgSVJzCgpgYGB7cn0KYnJlYWtzIDwtIHF1YW50aWxlKENUX2FnZ19ob21zJElSLCBwcm9icz1jKHNlcSgwLDEsMC4xNSksMSkpCkNUX3NocF9nZ19jb3VudF9hZ2cgPC0gQ1Rfc2hwX2dnWywtMV0gJT4lIGxlZnRfam9pbihDVF9hZ2dfaG9tc1ssYygxLDQpXSwgYnk9IkNEX0dFT0NPREkiKSAlPiUgbXV0YXRlKGN1dD1jdXQoSVIsIGJyZWFrcz1icmVha3MsIGluY2x1ZGUubG93ZXN0PVRSVUUpKQpwbG90X2ZvcnRfbWFwczIoQ1Rfc2hwX2dnX2NvdW50X2FnZywgImN1dCIsIkhvbWljaWRlIGNvdW50cyIsICJBZ2dyZWdhdGUgSVIgZm9yIDIwMDEtMTcgYnkgQ1QiKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCksIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKCkNvdW50IHBsb3RzIGJ5IHllYXIKYGBge3J9CmJyZWFrcyA8LSBjKDAsMSwyLDMsNCwxMCwzNSkKCkNUX3NocF9nZ19jb3VudCA8LSBDVF9zaHBfZ2dbLC0xXSAlPiUgbGVmdF9qb2luKHNwcmVhZChDVF9ob21zWyxjKDE6MyldLCBZT0QsIG9ic19jb3VudCksIGJ5PSJDRF9HRU9DT0RJIikgJT4lIG11dGF0ZV9hdCguZnVucyA9IGZ1bnMoY3V0ID0gY3V0KC4sIGJyZWFrcz1icmVha3MsIGluY2x1ZGUubG93ZXN0PVRSVUUpKSwgLnZhcnMgPSB2YXJzKGAyMDAxYDpgMjAxN2ApKQpgYGAKCmBgYHtyfQpjb3VudF9wbG90cyA8LSBzYXBwbHkoMjAwMToyMDE3LCBmdW5jdGlvbih4KSBwbG90X2ZvcnRfbWFwczIoQ1Rfc2hwX2dnX2NvdW50LCBwYXN0ZTAoImAiLHgsIl9jdXRgIiksIHBhc3RlMCgiSG9taWNpZGUgY291bnQgZm9yICIseCksIGFzLmNoYXJhY3Rlcih4KSwgc2l6ZT0wLjA1KSwgc2ltcGxpZnkgPSBGQUxTRSkKYGBgCgoKYGBge3J9CmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KGNvd3Bsb3QpCnBsb3QgPC0gY291bnRfcGxvdHNbWzFdXSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTUpLCBsZWdlbmQudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9NSksIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41LCJsaW5lIikpICsgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPSJIb21pY2lkZSBDb3VudCIpKQpteWxlZ2VuZCA8LSBnX2xlZ2VuZChwbG90KQpncmlkX2NvdW50IDwtIGRvLmNhbGwoKHBsb3RfZ3JpZCksIGMoY291bnRfcGxvdHMsIG5yb3c9NSkpICsgZHJhd19ncm9iKG15bGVnZW5kLCAwLjI4LCAwLjA4LCAwLjI4LCAwLjA4KSAKYGBgCgpgYGB7cn0KZ3JpZF9jb3VudApgYGAKCgpJUiBwbG90cyBieSB5ZWFycwoKYGBge3J9CmJyZWFrcyA8LSBjKDAsNSwxMCwxNSw1MCwxMDAsMjAwLDE1MDAwKQoKQ1Rfc2hwX2dnX0lSIDwtIENUX3NocF9nZ1ssLTFdICU+JSBsZWZ0X2pvaW4oc3ByZWFkKENUX2hvbXNbLGMoMSwyLDcpXSwgWU9ELCBJUl9wZXJfeXIpLCBieT0iQ0RfR0VPQ09ESSIpICU+JSBtdXRhdGVfYXQoLmZ1bnMgPSBmdW5zKGN1dCA9IGN1dCguLCBicmVha3M9YnJlYWtzLCBsYWJlbHM9KGMoIlswLDVdIiwiKDUsMTBdIiwiKDEwLDE1XSIsIigxNSw1MF0iLCIoNTAsMTAwXSIsIigxMDAsMjAwXSIsIj4yMDAiKSksIGluY2x1ZGUubG93ZXN0PVRSVUUpKSwgLnZhcnMgPSB2YXJzKGAyMDAxYDpgMjAxN2ApKQpgYGAKCmBgYHtyfQpJUl9wbG90cyA8LSBzYXBwbHkoMjAwMToyMDE3LCBmdW5jdGlvbih4KSBwbG90X2ZvcnRfbWFwczIoQ1Rfc2hwX2dnX0lSLCBwYXN0ZTAoImAiLHgsIl9jdXRgIiksIHBhc3RlMCgiSVIgZm9yICIseCksIGFzLmNoYXJhY3Rlcih4KSksIHNpbXBsaWZ5ID0gRkFMU0UpCgpwbG90IDwtIElSX3Bsb3RzW1sxXV0gKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT01KSwgbGVnZW5kLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTUpLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNSwibGluZSIpKSArIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZCh0aXRsZT0iSG9taWNpZGUgSVIiKSkKbXlsZWdlbmQgPC0gZ19sZWdlbmQocGxvdCkKZ3JpZF9JUiA8LSBkby5jYWxsKChwbG90X2dyaWQpLCBjKGNvdW50X3Bsb3RzLCBucm93PTUpKSArIGRyYXdfZ3JvYihteWxlZ2VuZCwgMC4yOCwgMC4wOCwgMC4yOCwgMC4wOCkKYGBgCgpgYGB7cn0KZ3JpZF9JUgpgYGAKClNNUiBwbG90cyBieSB5ZWFycwpgYGB7cn0KYnJlYWtzIDwtIGMoMCwxLDIsNSwxMCwxNSw1MCw0MDApCgpDVF9zaHBfZ2dfU01SIDwtIENUX3NocF9nZ1ssLTFdICU+JSBsZWZ0X2pvaW4oc3ByZWFkKENUX2hvbXNbLGMoMSwyLDYpXSwgWU9ELCBTTVJfcGVyX3lyKSwgYnk9IkNEX0dFT0NPREkiKSAlPiUgbXV0YXRlX2F0KC5mdW5zID0gZnVucyhjdXQgPSBjdXQoLiwgYnJlYWtzPWJyZWFrcywgbGFiZWxzPShjKCJbMCwxXSIsIigxLDJdIiwiKDIsNV0iLCIoNSwxMF0iLCIoMTAsMTVdIiwiKDE1LDUwXSIsIj41MCIpKSwgaW5jbHVkZS5sb3dlc3Q9VFJVRSkpLCAudmFycyA9IHZhcnMoYDIwMDFgOmAyMDE3YCkpCmBgYAoKYGBge3J9ClNNUl9wbG90cyA8LSBzYXBwbHkoMjAwMToyMDE3LCBmdW5jdGlvbih4KSBwbG90X2ZvcnRfbWFwczIoQ1Rfc2hwX2dnX1NNUiwgcGFzdGUwKCJgIix4LCJfY3V0YCIpLCBwYXN0ZTAoIklSIGZvciAiLHgpLCBhcy5jaGFyYWN0ZXIoeCkpLCBzaW1wbGlmeSA9IEZBTFNFKQoKcGxvdCA8LSBTTVJfcGxvdHNbWzFdXSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTUpLCBsZWdlbmQudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9NSksIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41LCJsaW5lIikpICsgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPSJIb21pY2lkZSBJUiIpKQpteWxlZ2VuZCA8LSBnX2xlZ2VuZChwbG90KQpncmlkX1NNUiA8LSBkby5jYWxsKChwbG90X2dyaWQpLCBjKGNvdW50X3Bsb3RzLCBucm93PTUpKSArIGRyYXdfZ3JvYihteWxlZ2VuZCwgMC4yOCwgMC4wOCwgMC4yOCwgMC4wOCkKYGBgCgpgYGB7cn0KZ3JpZF9TTVIKYGBgCgo=